From d291862c538badb4a1ccc709653335f42964fde9 Mon Sep 17 00:00:00 2001 From: "mjw@wray-m-3.hpl.hp.com" Date: Fri, 23 Jul 2004 15:38:32 +0000 Subject: [PATCH] bitkeeper revision 1.1108.1.17 (410130f82d--8rFf_j0nL2KMhoKYvg) Make rebooting a domain use the same domain id and console, without disconnecting any connected console. --- tools/python/xen/xend/XendConsole.py | 151 ++---------------- tools/python/xen/xend/XendDomain.py | 45 +++--- tools/python/xen/xend/XendDomainInfo.py | 34 +++- tools/python/xen/xend/server/SrvConsole.py | 6 +- tools/python/xen/xend/server/SrvConsoleDir.py | 5 +- tools/python/xen/xend/server/SrvDaemon.py | 7 +- tools/python/xen/xend/server/SrvDomain.py | 2 +- tools/python/xen/xend/server/console.py | 41 +++-- tools/python/xen/xend/server/controller.py | 2 +- tools/python/xen/xm/create.py | 2 +- 10 files changed, 116 insertions(+), 179 deletions(-) diff --git a/tools/python/xen/xend/XendConsole.py b/tools/python/xen/xend/XendConsole.py index 9cc57ff456..6825dc5baa 100644 --- a/tools/python/xen/xend/XendConsole.py +++ b/tools/python/xen/xend/XendConsole.py @@ -8,6 +8,7 @@ import sxp import XendRoot xroot = XendRoot.instance() import XendDB +from XendError import XendError import EventServer eserver = EventServer.instance() @@ -15,160 +16,38 @@ eserver = EventServer.instance() from xen.xend.server import SrvDaemon daemon = SrvDaemon.instance() -class XendConsoleInfo: - """Console information record. - """ - - def __init__(self, console, dom1, port1, dom2, port2, conn=None): - self.console = console - self.dom1 = int(dom1) - self.port1 = int(port1) - self.dom2 = int(dom2) - self.port2 = int(port2) - self.conn = conn - #self.id = "%d.%d-%d.%d" % (self.dom1, self.port1, self.dom2, self.port2) - self.id = str(port1) - - def __str__(self): - s = "console" - s += " id=%s" % self.id - s += " src=%d.%d" % (self.dom1, self.port1) - s += " dst=%d.%d" % (self.dom2, self.port2) - s += " port=%s" % self.console - if self.conn: - s += " conn=%s:%s" % (self.conn[0], self.conn[1]) - return s - - def sxpr(self): - sxpr = ['console', - ['id', self.id], - ['src', self.dom1, self.port1], - ['dst', self.dom2, self.port2], - ['port', self.console], - ] - if self.conn: - sxpr.append(['connected', self.conn[0], self.conn[1]]) - return sxpr - - def connection(self): - return self.conn - - def update(self, consinfo): - conn = sxp.child(consinfo, 'connected') - if conn: - self.conn = conn[1:] - else: - self.conn = None - - def uri(self): - """Get the uri to use to connect to the console. - This will be a telnet: uri. - - return uri - """ - host = socket.gethostname() - return "telnet://%s:%s" % (host, self.console) - class XendConsole: - dbpath = "console" - def __init__(self): - self.db = XendDB.XendDB(self.dbpath) - self.console = {} - self.console_db = self.db.fetchall("") - if xroot.get_rebooted(): - print 'XendConsole> rebooted: removing all console info' - self.rm_all() + pass eserver.subscribe('xend.domain.died', self.onDomainDied) eserver.subscribe('xend.domain.destroy', self.onDomainDied) - def rm_all(self): - """Remove all console info. Used after reboot. - """ - for (k, v) in self.console_db.items(): - self._delete_console(k) - - def refresh(self): - consoles = daemon.consoles() - cons = {} - for consinfo in consoles: - id = str(sxp.child_value(consinfo, 'id')) - cons[id] = consinfo - if id not in self.console: - self._new_console(consinfo) - for c in self.console.values(): - consinfo = cons.get(c.id) - if consinfo: - c.update(consinfo) - else: - self._delete_console(c.id) - def onDomainDied(self, event, val): - dom = int(val) - #print 'XendConsole>onDomainDied', 'event', event, "dom=", dom - for c in self.consoles(): - #print 'onDomainDied', "dom=", dom, "dom1=", c.dom1, "dom2=", c.dom2 - if (c.dom1 == dom) or (c.dom2 == dom): - 'XendConsole>onDomainDied', 'delete console dom=', dom - ctrl = daemon.get_domain_console(dom) - if ctrl: - ctrl.close() - self._delete_console(c.id) - - def sync(self): - self.db.saveall("", self.console_db) - - def sync_console(self, id): - self.db.save(id, self.console_db[id]) - - def _new_console(self, consinfo): - # todo: xen needs a call to get current domain id. - dom1 = 0 - port1 = sxp.child_value(consinfo, 'local_port') - dom2 = sxp.child_value(consinfo, 'domain') - port2 = sxp.child_value(consinfo, 'remote_port') - console = sxp.child_value(consinfo, 'console_port') - info = XendConsoleInfo(console, dom1, int(port1), int(dom2), int(port2)) - info.update(consinfo) - self._add_console(info.id, info) - return info - - def _add_console(self, id, info): - self.console[id] = info - self.console_db[id] = info.sxpr() - self.sync_console(id) - - def _delete_console(self, id): - if id in self.console: - del self.console[id] - if id in self.console_db: - del self.console_db[id] - self.db.delete(id) + pass def console_ls(self): - self.refresh() - return self.console.keys() + return [ c.console_port for c in self.consoles() ] def consoles(self): - self.refresh() - return self.console.values() + return daemon.get_consoles() def console_create(self, dom, console_port=None): consinfo = daemon.console_create(dom, console_port=console_port) - info = self._new_console(consinfo) - return info + return consinfo def console_get(self, id): - self.refresh() - return self.console.get(id) - - def console_delete(self, id): - self._delete_console(id) + id = int(id) + for c in self.consoles(): + if c.console_port == id: + return c + return None def console_disconnect(self, id): - id = int(id) - daemon.console_disconnect(id) + console = self.console_get(id) + if not console: + raise XendError('Invalid console id') + console.disconnect() def instance(): global inst diff --git a/tools/python/xen/xend/XendDomain.py b/tools/python/xen/xend/XendDomain.py index d064307a3a..f44b5c1ad5 100644 --- a/tools/python/xen/xend/XendDomain.py +++ b/tools/python/xen/xend/XendDomain.py @@ -18,7 +18,6 @@ import XendRoot xroot = XendRoot.instance() import XendDB import XendDomainInfo -import XendConsole import XendMigrate import EventServer from XendError import XendError @@ -42,14 +41,13 @@ class XendDomain: """Table of domain info indexed by domain id.""" domain = {} - """Table of configs for domain restart, indexed by domain id.""" + """Table of domains to restart, indexed by domain id.""" restarts = {} """Table of delayed calls.""" schedule = {} def __init__(self): - self.xconsole = XendConsole.instance() # Table of domain info indexed by domain id. self.db = XendDB.XendDB(self.dbpath) self.domain_db = self.db.fetchall("") @@ -322,6 +320,19 @@ class XendDomain: deferred.addCallback(fn) return deferred + def domain_restart(self, dominfo): + """Restart a domain. + + @param dominfo: domain object + @return: deferred + """ + deferred = dominfo.restart() + def fn(dominfo): + self._add_domain(dominfo.id, dominfo) + return dominfo + deferred.addCallback(fn) + return deferred + def domain_configure(self, id, config): """Configure an existing domain. This is intended for internal use by domain restore and migrate. @@ -405,7 +416,7 @@ class XendDomain: if reason == 'halt': self.domain_restart_cancel(id) else: - self.domain_restart_schedule(id, reason, set=1) + self.domain_restart_schedule(id, reason, force=1) eserver.inject('xend.domain.shutdown', [id, reason]) if reason == 'halt': reason = 'poweroff' @@ -413,25 +424,22 @@ class XendDomain: self.refresh_schedule() return val - def domain_restart_schedule(self, id, reason, set=0): + def domain_restart_schedule(self, id, reason, force=0): """Schedule a restart for a domain if it needs one. @param id: domain id @param reason: shutdown reason """ - log.debug('domain_restart_schedule> %s %s %d', id, reason, set) + log.debug('domain_restart_schedule> %s %s %d', id, reason, force) dominfo = self.domain.get(id) if not dominfo: return if id in self.restarts: return - if set and reason == 'reboot': - dominfo.restart_mode = XendDomainInfo.RESTART_ALWAYS - restart = dominfo.restart_needed(reason) + restart = (force and reason == 'reboot') or dominfo.restart_needed(reason) if restart: - # Avoid multiple restarts. - dominfo.restart_mode = XendDomainInfo.RESTART_NEVER - self.restarts[id] = dominfo.config + dominfo.restarting() + self.restarts[id] = dominfo log.info('Scheduling restart for domain: id=%s name=%s', id, dominfo.name) self.domain_restarts_schedule() @@ -440,10 +448,9 @@ class XendDomain: @param id: domain id """ - dominfo = self.domain.get(id) + dominfo = self.restarts.get(id) if dominfo: - dominfo.restart_mode = XendDomainInfo.RESTART_NEVER - if id in self.restarts: + dominfo.restart_cancel() del self.restarts[id] def domain_restarts(self): @@ -454,17 +461,17 @@ class XendDomain: if id in self.domain: # Don't execute restart for domains still running. continue - config = self.restarts[id] + dominfo = self.restarts[id] # Remove it from the restarts. del self.restarts[id] try: - log.info('domain_restarts> restart: id=%s config=%s', id, str(config)) + log.info('domain_restarts> restart: id=%s config=%s', id, str(dominfo.config)) def cbok(dominfo): log.info('Restarted domain %s as %s', id, dominfo.id) self.domain_unpause(dominfo.id) def cberr(err): log.exception("Delayed exception restarting domain") - deferred = self.domain_create(config) + deferred = self.domain_restart(dominfo) deferred.addCallback(cbok) deferred.addErrback(cberr) except: @@ -500,7 +507,7 @@ class XendDomain: if reason == 'halt': self.domain_restart_cancel(id) elif reason == 'reboot': - self.domain_restart_schedule(id, reason, set=1) + self.domain_restart_schedule(id, reason, force=1) val = self.final_domain_destroy(id) self.refresh_schedule() return val diff --git a/tools/python/xen/xend/XendDomainInfo.py b/tools/python/xen/xend/XendDomainInfo.py index a519645c1a..2a2743d3ef 100644 --- a/tools/python/xen/xend/XendDomainInfo.py +++ b/tools/python/xen/xend/XendDomainInfo.py @@ -62,6 +62,9 @@ restart_modes = [ RESTART_NEVER, ] +STATE_RESTART_PENDING = 'pending' +STATE_RESTART_BOOTING = 'booting' + def shutdown_reason(code): """Get a shutdown reason from a code. @@ -277,6 +280,7 @@ def vm_recreate(savedinfo, info): else: d = defer.Deferred() d.callback(vm) + vm.recreate = 0 return d def vm_restore(src, progress=0): @@ -360,8 +364,8 @@ class XendDomainInfo: self.state = self.STATE_OK #todo: set to migrate info if migrating self.migrate = None - #Whether to auto-restart self.restart_mode = RESTART_ONREBOOT + self.restart_state = None self.console_port = None def setdom(self, dom): @@ -380,7 +384,7 @@ class XendDomainInfo: s += " name=" + self.name s += " memory=" + str(self.memory) if self.console: - s += " console=" + self.console.id + s += " console=" + str(self.console.console_port) if self.image: s += " image=" + self.image s += "" @@ -536,6 +540,8 @@ class XendDomainInfo: devices have been released. """ if self.dom is None: return 0 + if self.restart_state == STATE_RESTART_PENDING and self.console: + self.console.deregisterChannel() chan = xend.getDomChannel(self.dom) if chan: log.debug("Closing channel to domain %d", self.dom) @@ -603,7 +609,8 @@ class XendDomainInfo: memory = self.memory name = self.name cpu = int(sxp.child_value(self.config, 'cpu', '-1')) - dom = xc.domain_create(mem_kb= memory * 1024, name= name, cpu= cpu) + dom = self.dom or 0 + dom = xc.domain_create(dom= dom, mem_kb= memory * 1024, name= name, cpu= cpu) if dom <= 0: raise VmError('Creating domain failed: name=%s memory=%d' % (name, memory)) @@ -627,7 +634,7 @@ class XendDomainInfo: if self.blkif_backend: flags |= SIF_BLK_BE_DOMAIN err = buildfn(dom = dom, image = kernel, - control_evtchn = self.console.port2, + control_evtchn = self.console.getRemotePort(), cmdline = cmdline, ramdisk = ramdisk, flags = flags) @@ -650,7 +657,10 @@ class XendDomainInfo: if ramdisk and not os.path.isfile(ramdisk): raise VmError('Kernel ramdisk does not exist: %s' % ramdisk) self.init_domain() - self.console = xendConsole.console_create(self.dom, console_port=self.console_port) + if self.console: + self.console.registerChannel() + else: + self.console = xendConsole.console_create(self.dom, console_port=self.console_port) self.build_domain(ostype, kernel, ramdisk, cmdline, vifs_n) self.image = kernel self.ramdisk = ramdisk @@ -737,6 +747,20 @@ class XendDomainInfo: return reason == 'reboot' return 0 + def restart_cancel(self): + self.restart_state = None + + def restarting(self): + self.restart_state = STATE_RESTART_PENDING + + def restart(self): + try: + self.restart_state = STATE_RESTART_BOOTING + d = self.construct(self.config) + finally: + self.restart_state = None + return d + def configure_backends(self): """Set configuration flags if the vm is a backend for netif of blkif. """ diff --git a/tools/python/xen/xend/server/SrvConsole.py b/tools/python/xen/xend/server/SrvConsole.py index eb8994425b..4105fa6e16 100644 --- a/tools/python/xen/xend/server/SrvConsole.py +++ b/tools/python/xen/xend/server/SrvConsole.py @@ -14,7 +14,7 @@ class SrvConsole(SrvDir): self.xc = XendConsole.instance() def op_disconnect(self, op, req): - val = self.xc.console_disconnect(self.info.id) + val = self.xc.console_disconnect(self.info.console_port) return val def render_POST(self, req): @@ -31,7 +31,7 @@ class SrvConsole(SrvDir): #self.ls() req.write('

%s

' % self.info) req.write('

Connect to domain %d

' - % (self.info.uri(), self.info.dom2)) + % (self.info.uri(), self.info.dom)) self.form(req) req.write('') return '' @@ -40,6 +40,6 @@ class SrvConsole(SrvDir): def form(self, req): req.write('
' % req.prePathURL()) - if self.info.connection(): + if self.info.connected(): req.write('') req.write('
') diff --git a/tools/python/xen/xend/server/SrvConsoleDir.py b/tools/python/xen/xend/server/SrvConsoleDir.py index 1371f6c0ef..139f302536 100644 --- a/tools/python/xen/xend/server/SrvConsoleDir.py +++ b/tools/python/xen/xend/server/SrvConsoleDir.py @@ -55,8 +55,9 @@ class SrvConsoleDir(SrvDir): sxp.show(consoles, out=req) else: consoles = self.xconsole.consoles() - consoles.sort(lambda x, y: cmp(x.id, y.id)) + consoles.sort(lambda x, y: cmp(x.console_port, y.console_port)) req.write('') diff --git a/tools/python/xen/xend/server/SrvDaemon.py b/tools/python/xen/xend/server/SrvDaemon.py index 1102baae1d..aaeef6d54c 100644 --- a/tools/python/xen/xend/server/SrvDaemon.py +++ b/tools/python/xen/xend/server/SrvDaemon.py @@ -121,7 +121,7 @@ class MgmtProtocol(protocol.DatagramProtocol): console_port = sxp.child_value(req, 'console_port') if console_port: console_port = int(console_port) - resp = self.daemon.console_create(dom, console_port) + resp = self.daemon.console_create(dom, console_port).sxpr() print name, resp return resp @@ -730,11 +730,14 @@ class Daemon: console = self.consoleCF.getInstanceByDom(dom) if console is None: console = self.consoleCF.createInstance(dom, console_port) - return console.sxpr() + return console def consoles(self): return [ c.sxpr() for c in self.consoleCF.getInstances() ] + def get_consoles(self): + return self.consoleCF.getInstances() + def get_console(self, id): return self.consoleCF.getInstance(id) diff --git a/tools/python/xen/xend/server/SrvDomain.py b/tools/python/xen/xend/server/SrvDomain.py index eed90b7feb..8eb5e12719 100644 --- a/tools/python/xen/xend/server/SrvDomain.py +++ b/tools/python/xen/xend/server/SrvDomain.py @@ -204,7 +204,7 @@ class SrvDomain(SrvDir): req.write('

%s

' % self.dom) if self.dom.console: cinfo = self.dom.console - cid = cinfo.id + cid = str(cinfo.console_port) #todo: Local xref: need to know server prefix. req.write('

Console %s

' % (cid, cid)) diff --git a/tools/python/xen/xend/server/console.py b/tools/python/xen/xend/server/console.py index 3c6325f8af..b3231beb12 100755 --- a/tools/python/xen/xend/server/console.py +++ b/tools/python/xen/xend/server/console.py @@ -1,5 +1,7 @@ # Copyright (C) 2004 Mike Wray +import socket + from twisted.internet import reactor from twisted.internet import protocol @@ -124,17 +126,38 @@ class ConsoleController(controller.Controller): self.listen() def sxpr(self): - val =['console', - ['status', self.status ], - ['id', self.idx ], - ['domain', self.dom ], - ['local_port', self.channel.getLocalPort() ], - ['remote_port', self.channel.getRemotePort() ], - ['console_port', self.console_port ] ] + val = ['console', + ['status', self.status ], + ['id', self.idx ], + ['domain', self.dom ] ] + val.append(['local_port', self.getLocalPort() ]) + val.append(['remote_port', self.getRemotePort() ]) + val.append(['console_port', self.console_port ]) if self.addr: val.append(['connected', self.addr[0], self.addr[1]]) return val + def getLocalPort(self): + if self.channel: + return self.channel.getLocalPort() + else: + return 0 + + def getRemotePort(self): + if self.channel: + return self.channel.getRemotePort() + else: + return 0 + + def uri(self): + """Get the uri to use to connect to the console. + This will be a telnet: uri. + + return uri + """ + host = socket.gethostname() + return "telnet://%s:%d" % (host, self.console_port) + def ready(self): return not (self.closed() or self.rbuf.empty()) @@ -222,7 +245,7 @@ class ConsoleController(controller.Controller): Writes as much to the channel as it can. """ work = 0 - while not self.wbuf.empty() and self.channel.writeReady(): + while self.channel and not self.wbuf.empty() and self.channel.writeReady(): msg = xu.message(CMSG_CONSOLE, 0, 0) msg.append_payload(self.wbuf.read(msg.MAX_PAYLOAD)) work += self.channel.writeRequest(msg, notify=0) @@ -240,7 +263,7 @@ class ConsoleController(controller.Controller): if self.closed(): return -1 if conn != self.conn: return 0 self.wbuf.write(data) - if self.produceRequests(): + if self.channel and self.produceRequests(): self.channel.notify() return 0 diff --git a/tools/python/xen/xend/server/controller.py b/tools/python/xen/xend/server/controller.py index e63585b212..756a7cd64e 100755 --- a/tools/python/xen/xend/server/controller.py +++ b/tools/python/xen/xend/server/controller.py @@ -199,7 +199,7 @@ class CtrlMsgRcvr: """ if self.channel: self.channel.deregisterDevice(self) - del self.channel + self.channel = None def produceRequests(self): """Produce any queued requests. diff --git a/tools/python/xen/xm/create.py b/tools/python/xen/xm/create.py index ce56b75d9c..3b7982f5e5 100644 --- a/tools/python/xen/xm/create.py +++ b/tools/python/xen/xm/create.py @@ -405,7 +405,7 @@ def make_domain(opts, config): dom = int(sxp.child_value(dominfo, 'id')) console_info = sxp.child(dominfo, 'console') if console_info: - console_port = int(sxp.child_value(console_info, 'port')) + console_port = int(sxp.child_value(console_info, 'console_port')) else: console_port = None -- 2.30.2